package org.jeecg.modules.system.service.impl;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.system.entity.FileUpload;
import org.jeecg.modules.system.entity.bo.ChunkUploadBo;
import org.jeecg.modules.system.entity.bo.FileSliceConfBo;
import org.jeecg.modules.system.mapper.FileUploadMapper;
import org.jeecg.modules.system.service.IFileUploadService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xy
 * @date 2024/2/8
 * @desc ***
 **/
@Service
public class FileUploadServiceImpl implements IFileUploadService {

    private final static Logger log = LoggerFactory.getLogger(FileUploadServiceImpl.class);

    @Value("${jeecg.path.upload}")
    private String BASE_PATH;

    @Resource
    private FileUploadMapper fileUploadMapper;

    @Override
    public Result<?> registerFileSliceInfo(FileSliceConfBo bo) {
        JSONObject resJson = new JSONObject();
        File dir = new File(BASE_PATH + File.separator + bo.getFileMd5());
        if (!dir.exists()) {
            boolean dirCreate = dir.mkdirs();
            if (!dirCreate) {
                return Result.error("创建分片文件目录失败，目录为：" + BASE_PATH + File.separator + bo.getFileMd5());
            }
        }
        QueryWrapper<FileUpload> confWrapper = new QueryWrapper<>();
        confWrapper.eq("md5", bo.getFileMd5());
        List<FileUpload> uploads = fileUploadMapper.selectList(confWrapper);
        if (!uploads.isEmpty()) {
            FileUpload upload = uploads.get(0);
            String fileName = upload.getName();
            String fileMd5 = upload.getMd5();
            if (!bo.getFileName().equals(fileName) || !bo.getFileMd5().equals(fileMd5)) {
                return Result.error("文件被篡改，不能续传，请先删除原有数据再重新上传");
            }
            boolean finish = upload.getFinish();
            if (finish) {
                resJson.put("fileName", upload.getName());
                resJson.put("path", upload.getPath());
                resJson.put("finish", finish);
                return Result.ok(resJson.toString());
            } else {
                resJson.put("chunkNum", upload.getChunkNum());
                resJson.put("chunkOffset", upload.getChunkOffset());
                resJson.put("finish", finish);
                return Result.ok(resJson.toString());
            }
        } else {
            JSONObject confJson = new JSONObject();
            confJson.put("fileName", bo.getFileName());
            confJson.put("fileMd5", bo.getFileMd5());
            confJson.put("chunkCount", bo.getTotalChunks());
            confJson.put("chunkSize", bo.getChunkSize());
            confJson.put("chunkNum", 1);
            confJson.put("chunkOffset", 0);
            confJson.put("finish", false);
            // 创建文件上传的注册信息
            FileUpload fileUpload = new FileUpload();
            fileUpload.setName(bo.getFileName());
            fileUpload.setTotalSize(bo.getFileSize());
            fileUpload.setMd5(bo.getFileMd5());
            fileUpload.setPath(BASE_PATH + File.separator + bo.getFileMd5() + File.separator + bo.getFileName());
            fileUpload.setChunkCount(bo.getTotalChunks());
            fileUpload.setChunkSize(bo.getChunkSize());
            fileUpload.setChunkNum(1L);
            fileUpload.setChunkOffset(0L);
            fileUpload.setFinish(false);
            fileUploadMapper.insert(fileUpload);
            return Result.ok(confJson.toString());
        }

    }

    @Override
    public Result<?> uploadChunk(ChunkUploadBo bo) {
        // 先检测分块是否存在，如果存在则直接返回
        File file = new File(BASE_PATH + File.separator + bo.getFileMd5() + File.separator + bo.getChunkNum());
        if (file.exists() && file.length() == bo.getChunkSize()) {
            return Result.ok("分片:" + bo.getChunkNum() + ",上传完成");
        }
        // 最后一个分片已上传完毕，还没有合并分片情况
        long lastChunkSize = bo.getFileSize() % bo.getChunkSize();
        if (file.exists() && bo.isLastChunk() && file.length() == lastChunkSize) {
            return Result.ok("分片全部上传完成,请发起合并请求");
        }

        // 获取该文件的注册信息
        QueryWrapper<FileUpload> fileWrapper = new QueryWrapper<>();
        fileWrapper.eq("md5", bo.getFileMd5());
        List<FileUpload> uploads = fileUploadMapper.selectList(fileWrapper);
        if (uploads.isEmpty()) {
            return Result.error("没有获取到文件上传注册信息，请重新上传");
        }
        FileUpload fileUpload = uploads.get(0);
        fileUpload.setChunkNum(bo.getChunkNum());
        //保存分片
        long chunkOffset = 0;
        try {
            InputStream ins = bo.getChunkFile().getInputStream();
            FileOutputStream ous = new FileOutputStream(file);
            byte[] readBytes = new byte[1024];
            //断点续传
            if (bo.getOffset() != null && bo.getOffset() > 0) {
                RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
                // 设置偏移量
                accessFile.seek(bo.getOffset());
                chunkOffset += bo.getOffset();
                while (ins.read(readBytes, bo.getOffset().intValue(), 1024) != -1) {
                    accessFile.write(readBytes);
                    chunkOffset += readBytes.length;
                }
                accessFile.close();
            } else {
                while (ins.read(readBytes) != -1) {
                    ous.write(readBytes);
                    ous.flush();
                    chunkOffset += readBytes.length;
                }
                ous.close();
                ins.close();
            }
        } catch (IOException e) {
            fileUpload.setChunkOffset(chunkOffset);
            fileUploadMapper.updateById(fileUpload);
            return Result.error("分片:" + bo.getChunkNum() + ",上传失败，请重试");
        }
        fileUpload.setChunkOffset(chunkOffset);
        fileUploadMapper.updateById(fileUpload);
        return Result.ok("分片:" + bo.getChunkNum() + ",上传成功!");
    }

    @Override
    public Result<?> mergeFile(FileSliceConfBo bo) {
        // 获取注册信息
        QueryWrapper<FileUpload> wrapper = new QueryWrapper<>();
        wrapper.eq("md5", bo.getFileMd5());
        List<FileUpload> uploads = fileUploadMapper.selectList(wrapper);
        if (uploads.isEmpty()) {
            return Result.error("无效的分片合并请求");
        }
        FileUpload fileUpload = uploads.get(0);
        if (fileUpload.getFinish()) {
            return Result.error("该文件已完成分片合并");
        }
        // 分片文件列表,后面删除分片文件使用
        List<File> chunkFileList = new ArrayList<>();
        // 合并分片
        File file = new File(BASE_PATH + File.separator + bo.getFileMd5() + File.separator + fileUpload.getName());
        try {
            FileOutputStream ous = new FileOutputStream(file);
            for (int i = 1; i <= fileUpload.getChunkCount(); i++) {
                byte[] bytes = new byte[1024];
                File chunkFile = new File(BASE_PATH + File.separator + bo.getFileMd5() + File.separator + i);
                if (!chunkFile.exists()) {
                    //修改当前分片为缺失的分片号，使其可以通过快传进行分片的续传
                    fileUpload.setChunkNum((long) i);
                    fileUpload.setChunkOffset(0L);
                    fileUploadMapper.updateById(fileUpload);
                    // 删除合并的文件，并返回错误
                    file.delete();
                    return Result.error("该文件分片数据不完整，无法合并，请重新上传");
                }
                chunkFileList.add(chunkFile);
                FileInputStream chunkIns = new FileInputStream(chunkFile);
                while (chunkIns.read(bytes) != -1) {
                    ous.write(bytes);
                }
                ous.flush();
                chunkIns.close();
            }
            ous.close();
        } catch (IOException e) {
            log.error("合并分片文件失败，文件路径为:{}，失败原因为:{}", BASE_PATH + File.separator + bo.getFileMd5(), e.getMessage());
            return Result.error("合并分片文件失败，失败原因:" + e.getMessage());
        }
        //全部合并完成，更新数据库并删除临时分片数据
//        fileUpload.setPath(file.getAbsolutePath());
        fileUpload.setPath("/"+ bo.getFileMd5() + "/" + fileUpload.getName());
        fileUpload.setFinish(true);
        fileUploadMapper.updateById(fileUpload);
        for (File chunkFile : chunkFileList) {
            chunkFile.delete();
        }
        Map<String,String> dataMap=new HashMap<>();
        dataMap.put("absolutePath",file.getAbsolutePath());
        dataMap.put("filePath",fileUpload.getPath());
        return Result.ok(dataMap);
    }

    @Override
    public Result<?> deleteFileInfo(FileSliceConfBo bo) {
        // 获取数据库对应文件数据
        QueryWrapper<FileUpload> queWrapper = new QueryWrapper<>();
        queWrapper.eq("name", bo.getFileName());
        queWrapper.eq("total_size", bo.getFileSize());
        List<FileUpload> uploads = fileUploadMapper.selectList(queWrapper);
        if (uploads.isEmpty()) {
            log.error("删除已上传文件没有找到记录，文件名为:{},文件大小为:{}", bo.getFileName(), bo.getFileSize());
            return Result.ok("成功");
        }
        for (FileUpload upload : uploads) {
            // 删除文件服务器数据
            File dir = new File(BASE_PATH + File.separator + upload.getMd5());
            FileUtil.del(dir);

            //删除数据库数据
            Integer delete = fileUploadMapper.deleteById(upload.getId());
            if (delete < 1) {
                return Result.error("没有该文件的数据，无需删除");
            }
        }
        return Result.ok("成功");
    }

}
